﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CarHelpDesk.Web.Utils;
using System.Transactions;

namespace CarHelpDesk.Web.Models.Repositories.LinqRepositories
{
    public class QuestionsRepository : IQuestionsRepository
    {
        #region Constants

        //TODO: store constants in DB
        public static readonly int VOTE_UP_VALUE = 10;
        public static readonly int VOTE_DOWN_VALUE = -2;

        #endregion

        #region Fields
        private LinqModelDataContext _db;
        private UserDetailsRepository userRepo;
        #endregion

        #region Constructors
        public QuestionsRepository()
        {
            this._db = new LinqModelDataContext();
#if DEBUG
            this._db.Log = DebugTextWriter.Instance;
#endif
            this.userRepo = new UserDetailsRepository(this._db);
        }
        public QuestionsRepository(bool isTest)
        {
            if (isTest)
                _db = new LinqModelDataContext(CarHelpDesk.Web.Models.Properties.Settings.Default.db_helpdesk_testConnectionString);
            else
                this._db = new LinqModelDataContext();
#if DEBUG
            this._db.Log = DebugTextWriter.Instance;
#endif
            this.userRepo = new UserDetailsRepository(this._db);
        }
        #endregion

        #region IQuestionsRepository Members

        public IQueryable<Question> GetQuestions()
        {
            return from p in _db.Questions
                   orderby p.CreateDate descending
                   select p;
        }

        public void Create(Question question)
        {
            question.CreateDate = DateTime.Now;
            question.UpdateDate = DateTime.Now;
            question.Active = true;

            _db.Questions.InsertOnSubmit(question);
            _db.SubmitChanges();
        }

        public Question GetByID(int QuestionID)
        {
            return this._db.Questions.FirstOrDefault(q => q.ID == QuestionID);
        }

        public IQueryable<Question> SearchByText(string text)
        {
            return from p in _db.Questions
                   where p.Title.Contains(text) || p.Content.Contains(text)
                   orderby p.CreateDate descending
                   select p;

        }

        /// <summary>
        /// Marks the question as favourite for given user and returns its current state.
        /// </summary>
        /// <param name="favQuestion">The fav question.</param>
        /// <returns>
        /// 	<c>true</c> if question is currently favourited; otherwise, <c>false</c>
        /// </returns>
        public bool QuestionToogleFavourite(FavouriteQuestion favQuestion)
        {
            bool result;
            var favQtn = from favQ in _db.FavouriteQuestions
                         where favQ.UserID == favQuestion.UserID && favQ.QuestionID == favQuestion.QuestionID
                         select favQ;

            if (favQtn.Count() == 1)
            {
                _db.FavouriteQuestions.DeleteOnSubmit(favQtn.First());
                _db.SubmitChanges();
                result = false;
            }
            else
            {
                _db.FavouriteQuestions.InsertOnSubmit(favQuestion);
                _db.SubmitChanges();
                result = true;
            }

            return result;
        }

        /// <summary>
        /// Determines whether [is favourited by] [the specified question ID].
        /// </summary>
        /// <param name="questionID">The question ID.</param>
        /// <param name="userID">The user ID.</param>
        /// <returns>
        /// 	<c>true</c> if [is favourited by] [the specified question ID]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsFavouritedBy(int questionID, Guid userID)
        {
            var qry = from p in _db.FavouriteQuestions
                      where p.QuestionID == questionID && p.UserID == userID
                      select p;

            return qry.Count() > 0;
        }

        /// <summary>
        /// Gets the questions favourited by the specified user.
        /// </summary>
        /// <param name="userID">The user's GUID.</param>
        /// <returns></returns>
        public IQueryable<Question> GetQuestionsFavouritedBy(Guid userID)
        {
            return from q in _db.FavouriteQuestions
                   where q.UserID == userID
                   select q.Question;
        }

        /// <summary>
        /// Gets QuestionVote created by user.
        /// </summary>
        /// <param name="question">The question.</param>
        /// <param name="userID">The user ID.</param>
        /// <returns>QuestionVote ot null if there is none</returns>
        public QuestionVote GetQuestionVote(Question question, Guid userID)
        {
            return question.QuestionVotes.FirstOrDefault(vote => vote.UserID == userID);
        }

        /// <summary>
        /// Creates the vote for question.
        /// </summary>
        /// <param name="vote">The vote.</param>
        public void CreateQuestionVote(QuestionVote vote)
        {
            if (vote.VoteValue != 1 && vote.VoteValue != -1)
                throw new ArgumentException("VoteValue should be 1 or -1", "VoteValue");

            using (var transaction = new TransactionScope())
            {
                Question question = this._db.Questions.First(q => q.ID == vote.QuestionID);
                question.QuestionVotes.Add(vote);

                if (vote.VoteValue == 1)
                {
                    question.VoteUpCount++;
                    this.userRepo.addToUserRating(question.aspnet_User, VOTE_UP_VALUE);
                }
                else
                {
                    question.VoteDownCount++;
                    this.userRepo.addToUserRating(question.aspnet_User, VOTE_DOWN_VALUE);
                }

                _db.SubmitChanges();
                transaction.Complete();
            }
        }

        public IQueryable<Question> SearchInAswers(string text)
        {
            return (from p in _db.Questions
                    join a in _db.Answers on p.ID equals a.QuestionID
                    where a.Content.Contains(text)
                    orderby p.CreateDate descending
                    select p).Distinct();

        }

        public IQueryable<Question> SearchByTag(List<Tag> tags)
        {
            List<Question> questions = new List<Question>();
            foreach (var cat in tags)
            {


                questions.AddRange((from p in _db.Questions
                                    join c in _db.QuestionTags on p.ID equals c.QuestionID
                                    where c.TagID == cat.ID
                                    orderby p.CreateDate descending
                                    select p).Distinct());
            }

            return questions.AsQueryable<Question>();
        }

        public void UpdateQuestionContentById(int id, string content)
        {
            Question question = GetByID(id);
            question.Content = content;
            _db.SubmitChanges();
        }

        public QuestionVote GetQuestionVote(int id)
        {
            return (from p in _db.QuestionVotes
                   where p.QuestionID == id
                   select p).SingleOrDefault();
        }

        public void DeleteQuestionById(int id)
        {
            Question question = GetByID(id);
            question.Active = false;

            QuestionVote questionVote = GetQuestionVote(id);
            questionVote.VoteValue -= 1;

            _db.SubmitChanges();
        }

        public void RevertQuestionById(int id)
        {
            Question question = GetByID(id);
            question.Active = true;

            _db.SubmitChanges();
        }

        /// <summary>
        /// Delete all items in FavouriteQuestion table in the database by questionID
        /// </summary>
        /// <param name="id">question ID</param>
        public void DeleteFavouriteQuestionsByQuestionID(int id)
        {
            List<FavouriteQuestion> favouriteQuestions = (from p in _db.FavouriteQuestions
                                                          where p.QuestionID == id
                                                          select p).ToList();
            foreach (FavouriteQuestion q in favouriteQuestions)
            {
                _db.FavouriteQuestions.DeleteOnSubmit(q);
            }

            _db.SubmitChanges();
        }

        /// <summary>
        /// Delete all items in ImageQuestion table in the database by questionID
        /// </summary>
        /// <param name="id">question ID</param>
        public void DeleteImageQuestion(int id)
        {
            List<ImageQuestion> imageQuestions = (from p in _db.ImageQuestions
                                                  where p.QuestionID == id
                                                  select p).ToList();
            foreach (ImageQuestion i in imageQuestions)
            {
                _db.ImageQuestions.DeleteOnSubmit(i);

                Image image = (from p in _db.Images
                               where p.ID == i.ImageID
                               select p).SingleOrDefault();
                _db.Images.DeleteOnSubmit(image);
            }

            _db.SubmitChanges();
        }

        /// <summary>
        /// Delete all items in QuestionAttentionFlags table in the database by questionID
        /// </summary>
        /// <param name="id">question ID</param>
        public void DeleteQuestionAttemptFlag(int id)
        {
            List<QuestionAttentionFlag> questionAttemptFlags = (from p in _db.QuestionAttentionFlags
                                                                where p.QuestionID == id
                                                                select p).ToList();
            foreach (QuestionAttentionFlag q in questionAttemptFlags)
            {
                _db.QuestionAttentionFlags.DeleteOnSubmit(q);
            }

            _db.SubmitChanges();
        }

        /// <summary>
        /// Delete all items in QuestionTags table in the database by questionID
        /// </summary>
        /// <param name="id">question ID</param>
        public void DeleteQuestionTags(int id)
        {
            List<QuestionTag> questionTags = (from p in _db.QuestionTags
                                              where p.QuestionID == id
                                              select p).ToList();
            foreach (QuestionTag q in questionTags)
            {
                _db.QuestionTags.DeleteOnSubmit(q);
            }

            _db.SubmitChanges();
        }

        /// <summary>
        /// Delete all items in QuestionVotes table in the database by questionID
        /// </summary>
        /// <param name="id">question ID</param>
        public void DeleteQuestionVotes(int id)
        {
            List<QuestionVote> questionVotes = (from p in _db.QuestionVotes
                                                where p.QuestionID == id
                                                select p).ToList();
            foreach (QuestionVote q in questionVotes)
            {
                _db.QuestionVotes.DeleteOnSubmit(q);
            }

            _db.SubmitChanges();
        }

        /// <summary>
        /// Delete a question from the database.
        /// </summary>
        /// <param name="id">qiestion ID</param>
        public void ModeratorDeleteQuestionById(int id)
        {   
            DeleteFavouriteQuestionsByQuestionID(id);

            DeleteImageQuestion(id);

            DeleteQuestionAttemptFlag(id);

            DeleteQuestionTags(id);

            DeleteQuestionVotes(id);

            Question question = GetByID(id);
            _db.Questions.DeleteOnSubmit(question);
            _db.SubmitChanges();
        }

        public bool QuestionToggleFlagged(QuestionAttentionFlag flaggedQuestion)
        {
            bool result;
            var favQtn = from flagQ in _db.QuestionAttentionFlags
                         where flagQ.UserID == flaggedQuestion.UserID && flagQ.QuestionID == flaggedQuestion.QuestionID
                         select flagQ;

            if (favQtn.Count() == 1)
            {
                _db.QuestionAttentionFlags.DeleteOnSubmit(favQtn.First());
                _db.SubmitChanges();
                result = false;
            }
            else
            {
                _db.QuestionAttentionFlags.InsertOnSubmit(flaggedQuestion);
                _db.SubmitChanges();
                result = true;
            }

            return result;
        }

        public IQueryable<Question> GetQuestionsMarkedAsFlagged()
        {
            return (from markedQuestions in _db.QuestionAttentionFlags
                   select markedQuestions.Question).Distinct() ;
        }

        #endregion
    }
}
